package org.wxh.springbootredis;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.wxh.springbootredis.entity.Book;

import java.beans.IntrospectionException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@SpringBootTest
class SpringBootRedisApplicationTests {
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    RedisTemplate redisTemplate;


    @Test
    void contextLoads() {
    }

    //存储字符串类型的key-value
    @Test
    void textKeyV() {
        stringRedisTemplate.opsForValue().set("username", "xiao");
    }

    //通过键获取字符串类型的值
    @Test
    void getValue() {
        String username = stringRedisTemplate.opsForValue().get("username");
        System.out.println(username);
    }

    //删除某个键值对
    @Test
    void delBook() {
        stringRedisTemplate.delete("username");
    }

    //保存一本书
    @Test
    void saveBook() {
        Book book = new Book(1, "三国演义", 100.00, "文学", 100, "101.png", "四大名著", "罗贯中", 55);
        redisTemplate.opsForValue().set(1, book);
    }

    //通过id号查找一本书
    @Test
    void getBook() {
        Book book = (Book) redisTemplate.opsForValue().get(1);
        System.out.println(book);
    }

    //保存多本书 list
    @Test
    void saveBooks() {
        List<Book> books = Arrays.asList(
                new Book(1, "C语言程序设计", 50.0, "计算机", 100, "101.jpg", "", "zhangsan", 50),
                new Book(2, "java语言程序设计", 60.0, "计算机", 100, "102.jpg", "", "zhangsan", 50),
                new Book(3, "python语言程序设计", 70.0, "计算机", 100, "103.jpg", "", "zhangsan", 50)
        );
        redisTemplate.opsForValue().set("books", books);
    }

    //查找所有书
    @Test
    void selectBook() {
        System.out.println(redisTemplate.opsForValue().get("books"));
    }

    //从多本book集合中找到某个id的书
    @Test
    void getBookId() {
        Integer id = 1;
        List<Book> books = (List<Book>) redisTemplate.opsForValue().get("books");
        Book book = books.get(id - 1);
        System.out.println(book);
    }

    //根据id删除book集合中的某本书
    @Test
    void delBookId() {
        Integer id = 1;
        List<Book> books = (List<Book>) redisTemplate.opsForValue().get("books");
        books.remove(id - 1);
        redisTemplate.opsForValue().set("books", books);
    }

    //测试opsForValue——set——时间测试，一旦超过时间，就好被Redis删除
    @Test
    void text1() throws InterruptedException {
        redisTemplate.opsForValue().set("test1", "Test Timeout 5 seconds", 5, TimeUnit.SECONDS);
        System.out.println("第0秒取值：" + redisTemplate.opsForValue().get("test1"));
        Thread.sleep(4000);
        System.out.println("第4秒取值：" + redisTemplate.opsForValue().get("test1"));
        Thread.sleep(2000);
        System.out.println("第6秒取值：" + redisTemplate.opsForValue().get("test1"));
    }

    //getAndSet——先获取并返回key的旧值，再设置为新值
    @Test
    void test1() throws InterruptedException {
        redisTemplate.opsForValue().set("test1", "Test getAndSet1");
        System.out.println(redisTemplate.opsForValue().getAndSet("test1", "get getAndSet2"));
        System.out.println(redisTemplate.opsForValue().get("test1"));
    }

    //append——如果key不存在则创建，相当于set方法，如果key存在则追加字符串到尾末
    @Test
    void test2() {
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.opsForValue().append("test2", "test2");
        System.out.println(redisTemplate.opsForValue().get("test2"));
        redisTemplate.opsForValue().append("test2", " OK!");
        System.out.println(redisTemplate.opsForValue().get("test2"));
    }

    /*
     opsForHash()方法

     void put(H h,HK hk,HV hv)方法：用于存储指定键中的字段与值。
     Map<HK , HV>entries(H h)方法：获取指定键的所有字段与值
     */
    @Test
    void testHash1() {
        redisTemplate.opsForHash().put("book1", "bookname", "三国演义");
        redisTemplate.opsForHash().put("book1", "price", "69.9");
        System.out.println(redisTemplate.opsForHash().entries("book1"));
    }

    //putAll——
    // void putAll(H h,java.util.Map<?extends HK,? extends HV>map)方法：使用Map封装多个字段与值，一次性存储多个字段与值
    @Test
    void testHash2() {
        Map<String, Object> map = new HashMap<>();
        map.put("bookname", "西游记");
        map.put("category", "文学");
        redisTemplate.opsForHash().putAll("book2", map);
        System.out.println(redisTemplate.opsForHash().entries("book2"));
    }

    /**
     * keys
     * Set<HK>keys(H h)方法：获取指定键的字段的集合
     */
    @Test
    void testHash3() {
        redisTemplate.opsForHash().put("book3", "bookname", "香蜜沉沉烬如霜");
        redisTemplate.opsForHash().put("book3", "price", "100.9");
        System.out.println(redisTemplate.opsForHash().keys("book3"));
    }

    /**
     * values
     * List<HV>values(H h)方法：获取指定键的值集合
     */
    @Test
    void testHash4() {
        redisTemplate.opsForHash().put("book4", "bookname", "微微一笑很倾城");
        redisTemplate.opsForHash().put("book4", "price", "50.5");
        System.out.println(redisTemplate.opsForHash().values("book4"));
    }

    /**
     * size、hasKey、delete
     * Long size(H h)方法：返回指定键中字段的数量
     * Boolean hasKey(H h,Object o)方法：查找指定键中是否包含某个字段
     * Long delete(H h,Object....Object)方法：删除指定键中的某个字段
     */
    @Test
    void testHash5() {
        redisTemplate.opsForHash().put("book5", "bookname", "微微一笑很倾城");
        redisTemplate.opsForHash().put("book5", "price", "50.5");
        System.out.println(redisTemplate.opsForHash().entries("book5"));
        System.out.println(redisTemplate.opsForHash().size("book5"));
        System.out.println(redisTemplate.opsForHash().hasKey("book5", "price"));
        redisTemplate.opsForHash().delete("book5", "price");
        System.out.println(redisTemplate.opsForHash().hasKey("book5", "price"));
        System.out.println(redisTemplate.opsForHash().entries("book5"));
    }

    //opsForList()方法

    /**
     * leftPush:从左侧添加一个元素到列表
     * Long leftPush(K k,V v)方法：从左侧添加一个元素到列表
     * Long leftPushAll(K,k,V ...vs)方法：从左侧一次添加多个元素到列表
     * Long size(K k):获取集合中的元素个数
     * java.util.List<V>range(K k,Long l,Long l1):返回某个列表指定索引区间的元素。索引从0算起，-1表示最右侧第一个元素
     */
    @Test
    void testList1() {
        redisTemplate.opsForList().leftPush("myList1", "a");
        redisTemplate.opsForList().leftPush("myList1", "b");
        redisTemplate.opsForList().leftPush("myList1", "c");
        System.out.println(redisTemplate.opsForList().size("myList1"));
        System.out.println(redisTemplate.opsForList().range("myList1", 0, -1));
        redisTemplate.opsForList().leftPushAll("myList1", "1", "2");
        String[] str = {"3", "4", "5"};
        redisTemplate.opsForList().leftPushAll("myList1", str);
        System.out.println(redisTemplate.opsForList().size("myList1"));
        System.out.println(redisTemplate.opsForList().range("myList1", 0, -1));
    }

    /**
     * rightPushAll：从右侧添加多个元素到列表
     * Long rightPushAll(K,k,V...vs):从右侧一次添加多个元素到列表
     */
    @Test
    void testList2() {
        redisTemplate.opsForList().rightPush("myList2", "a");
        redisTemplate.opsForList().rightPush("myList2", "b");
        redisTemplate.opsForList().rightPush("myList2", "c");
        System.out.println(redisTemplate.opsForList().size("myList2"));
        System.out.println(redisTemplate.opsForList().range("myList2", 0, -1));
        redisTemplate.opsForList().rightPushAll("myList2", "1", "2");
        String[] str = {"3", "4", "5"};
        redisTemplate.opsForList().rightPushAll("myList2", str);
        System.out.println(redisTemplate.opsForList().size("myList2"));
        System.out.println(redisTemplate.opsForList().range("myList2", 0, -1));
    }

    /**
     * remove:删除集合中指定数量个元素
     * Long remove(K k,Long l,Object 0):删除集合中指定数量个元素，如果数量为0，
     * 则该元素全部从集合删除，如果数量为正数，则从左到右删除，如果为负熟，则从右到左删除
     */

    @Test
    void testList3() {
        String[] str = {"10", "20", "30", "10", "20", "30", "10", "20", "30"};
        redisTemplate.opsForList().rightPushAll("myList3", str);
        System.out.println(redisTemplate.opsForList().range("myList3", 0, -1));
        redisTemplate.opsForList().remove("myList3", 0, "10");
        System.out.println(redisTemplate.opsForList().range("myList3", 0, -1));
        redisTemplate.opsForList().remove("myList3", 2, "20");
        System.out.println(redisTemplate.opsForList().range("myList3", 0, -1));
        redisTemplate.opsForList().remove("myList3", -2, "30");
        System.out.println(redisTemplate.opsForList().range("myList3", 0, -1));
    }

    /**
     * leftPop和rightPop:删除并返回左侧、右侧第一个元素
     * V leftPop(K k):删除并返回左侧第一个元素
     * V rightPop(K k):删除并返回右侧第一个元素
     */
    @Test
    void testList4() {
        String[] str = {"a", "b", "c", "d", "e"};
        redisTemplate.opsForList().rightPushAll("myList4", str);
        System.out.println(redisTemplate.opsForList().range("myList4", 0, -1));
        System.out.println(redisTemplate.opsForList().leftPop("myList4"));
        System.out.println(redisTemplate.opsForList().range("myList4", 0, -1));
        System.out.println(redisTemplate.opsForList().rightPop("myList4"));
        redisTemplate.opsForList().range("myList4", 0, -1);
    }

    /**
     * index:获取指定索引处的元素值set:为指定索引处的元素设置值，原有值将被覆盖
     * V index(K k,Long l):获取指定索引处的元素值
     * void set(K k,Long l,V v):为指定索引处的元素设置值，原有值被覆盖
     */
    @Test
    void testList5() {
        String[] str = {"a", "b", "c"};
        redisTemplate.opsForList().rightPushAll("myList5", str);
        System.out.println(redisTemplate.opsForList().range("myList5", 0, -1));
        System.out.println(redisTemplate.opsForList().index("myList5", 1));
        redisTemplate.opsForList().set("myList5", 1, "d");
        System.out.println(redisTemplate.opsForList().range("myList5", 0, -1));
        System.out.println(redisTemplate.opsForList().index("myList5", 1));
    }

    //opsForSet()方法

    /**
     * add size members remove pop
     * Long add(K k,V... vs)方法：添加数据（元素）到集合
     * Long size(K k)方法：返回集合长度，既集合中元素的个数
     * java.util.Set<V>members(K k):返回集合中的所有元素
     * Long remove(K k,Object... object):删除元素
     * V pop(K k):随机删除
     */
    @Test
    void testForSet1() {
        redisTemplate.opsForSet().add("mySet1", "a");
        redisTemplate.opsForSet().add("mySet1", "b", "c");
        String[] str = {"1", "2"};
        redisTemplate.opsForSet().add("mySet1", str);
        System.out.println("size:" + redisTemplate.opsForSet().size("mySet1"));
        System.out.println(redisTemplate.opsForSet().members("mySet1"));
        System.out.println(redisTemplate.opsForSet().remove("mySet1", "a"));
        System.out.println(redisTemplate.opsForSet().members("mySet1"));
        System.out.println(redisTemplate.opsForSet().remove("mySet1", str));
        System.out.println(redisTemplate.opsForSet().members("mySet1"));
        redisTemplate.opsForSet().add("mySet1", str);
        System.out.println(redisTemplate.opsForSet().members("mySet1"));
        System.out.println(redisTemplate.opsForSet().pop("mySet1"));
        redisTemplate.opsForSet().members("mySet1");
    }

    /**
     * move:移动元素到另一个集合
     * Boolean move(K k,V v,K k1)方法：移动元素到另一个集合
     */
    @Test
    void testForSet2() {
        String[] str1 = {"1", "2", "3"};
        String[] str2 = {"a", "b", "c"};
        System.out.println(redisTemplate.opsForSet().add("mySet2", str1));
        System.out.println(redisTemplate.opsForSet().add("mySet3", str2));
        System.out.println(redisTemplate.opsForSet().members("mySet2"));
        System.out.println(redisTemplate.opsForSet().members("mySet3"));
        redisTemplate.opsForSet().move("mySet2", "1", "mySet3");
        System.out.println(redisTemplate.opsForSet().members("mySet2"));
        System.out.println(redisTemplate.opsForSet().members("mySet3"));
    }

    /**
     * scan:遍历集合中所有元素
     * Cursor<V>scan(K k,ScanOptions scanOptions)
     */
    @Test
    void testForSet4() {
        String[] str = {"a", "b", "c"};
        redisTemplate.opsForSet().add("mySet4", str);
        Cursor<Object> cursor = redisTemplate.opsForSet().scan("mySet4", ScanOptions.NONE);
        while (cursor.hasNext()) {
            System.out.println(cursor.next());
        }
    }

    /**
     * opsForZSet()方法
     * Boolean add(K k,V v,double v1):添加元素
     * Long size(K k):返回集合中元素的个数
     * Set<V>range(K k,Long l,Long l1):返回指定索引范围的元素
     * Long rank(K k,Object o):返回某个元素的索引
     */
    @Test
    void testZSet1() {
        redisTemplate.opsForZSet().add("myZSet1", "f1", 80);
        redisTemplate.opsForZSet().add("myZSet1", "f2", 70);
        redisTemplate.opsForZSet().add("myZSet1", "f3", 90);
        System.out.println("size" + redisTemplate.opsForZSet().size("myZSet1"));
        System.out.println(redisTemplate.opsForZSet().range("myZSet1", 0, -1));
        System.out.println(redisTemplate.opsForZSet().rank("myZSet1", "f1"));
    }

    /**
     * score:返回某个元素的分数
     * Double score(K k,Object o):返回某个元素的分数
     * Set<v>rangeByScore(K k,double v,double v1):返回指定分数范围的元素
     * Long count(K k,double v,double v1):返回指定分数范围的元素的个数
     */
    @Test
    void testZSet2() {
        redisTemplate.opsForZSet().add("myZSet2", "f1", 80);
        redisTemplate.opsForZSet().add("myZSet2", "f2", 60);
        redisTemplate.opsForZSet().add("myZSet2", "f3", 70);
        redisTemplate.opsForZSet().add("myZSet2", "f4", 90);
        System.out.println(redisTemplate.opsForZSet().score("myZSet2", "f1"));
        System.out.println(redisTemplate.opsForZSet().range("myZSet2", 0, -1));

        System.out.println(redisTemplate.opsForZSet().rangeByScore("myZSet2", 70, 90));
        System.out.println(redisTemplate.opsForZSet().count("myZSet2", 70, 90));
    }

    /**
     * remove:删除指定元素
     */
    @Test
    void testZSet3() {
        redisTemplate.opsForZSet().add("myZSet3", "f1", 40);
        redisTemplate.opsForZSet().add("myZSet3", "f2", 50);
        redisTemplate.opsForZSet().add("myZSet3", "f3", 60);
        redisTemplate.opsForZSet().add("myZSet3", "f4", 70);
        redisTemplate.opsForZSet().add("myZSet3", "f5", 80);
        redisTemplate.opsForZSet().add("myZSet3", "f6", 90);
        redisTemplate.opsForZSet().add("myZSet3", "f7", 100);
        //降序排列
        redisTemplate.opsForZSet().reverseRange("myZSet3", 0, -1);

        System.out.println(redisTemplate.opsForZSet().range("myZSet3", 0, -1));
        redisTemplate.opsForZSet().remove("myZSet3", "f1");
        System.out.println(redisTemplate.opsForZSet().range("myZSet3", 0, -1));
        redisTemplate.opsForZSet().removeRange("myZSet3", 0, 1);
        System.out.println(redisTemplate.opsForZSet().range("myZSet3", 0, -1));
        redisTemplate.opsForZSet().removeRangeByScore("myZSet3", 70, 80);
        System.out.println(redisTemplate.opsForZSet().range("myZSet3", 0, -1));
    }
}
